Release 1.4.2a1#54
Open
github-actions[bot] wants to merge 38 commits into
Open
Conversation
…-workshop-9 chore(ovos_adapt_parser): allow ovos-workshop<9.0.0
* feat(test): ovoscope end-to-end tests for AdaptPipeline Adds an end-to-end test suite for AdaptPipeline using ovoscope's E2EPipelineHarness. Covers: - vocab + IntentBuilder registration and required-keyword matching - missing-keyword no-match and no-intent no-match - best-of-multiple-intent selection - optional slots (present / absent) - detach_intent and detach_skill isolation - session blacklisted_intents / blacklisted_skills CI job uses the shared OpenVoiceOS/gh-automations reusable workflow with require_adapt: true and pre_release: true (the latter is temporary until ovoscope ships a release containing the E2EPipelineHarness). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): avoid Adapt stop-word filtering in test_utterance_field_preserved Replace "on"/"turn on the lights" with "enable"/"enable the lights" so Adapt does not silently drop the keyword as a stop word, causing the send_and_capture to return None. Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com> * ci: modernize legacy workflows so PR checks actually run - Delete build_tests.yml and install_tests.yml: both reference mycroft-core-style extras (`[audio-backend,mark1,stt,tts,skills,gui, bus,all]`, `tflite_runtime`) that have no relevance to this library and have been failing for as long as Python 3.8 has been removed from GitHub runners. - unit_tests.yml: bump python matrix from [3.7,3.8,3.9,'3.10'] to ['3.10','3.11','3.12'] (3.7/3.8 are no longer available); upgrade actions/checkout v2 -> v6 and setup-python v2 -> v6. - license_tests.yml: same actions bumps; python 3.8 -> 3.11. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(license): upgrade action and drop 'Error' from fail list pilosus/action-pip-license-checker@v0.5.0 misclassifies modern packages (build, click, packaging, urllib3, etc.) as `Error` because it does not understand the current PEP 639/SPDX license metadata format. Upgrading to v2.6.2 fixes the classifier; dropping `Error` from the fail list prevents false positives when a single transitive dep can't be parsed. Copyleft and Other still fail — those are the categories the check is actually meant to catch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(license): pin action to v3.1.0 (v2.6.2 doesn't exist) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci: migrate to pyproject.toml + apply shared gh-automations workflows Drop the legacy setup.py / requirements.txt / MANIFEST.in in favour of a single pyproject.toml. Append the __version__ assignment to ovos_adapt/version.py so setuptools' attr-based dynamic versioning can read it. Replace the legacy mycroft-style PR workflows with the standard OpenVoiceOS/gh-automations reusable workflows: build-tests.yml coverage.yml license_check.yml lint.yml pip_audit.yml release-preview.yml repo-health.yml opm-check.yml conventional-label.yml publish_stable.yml release_workflow.yml Removed: unit_tests.yml, license_tests.yml (replaced by build-tests.yml and license_check.yml from gh-automations). build_tests.yml and install_tests.yml had already been deleted earlier in this branch as mycroft-core cruft. opm-check.yml uses plugin_type=pipeline (and drops the buggy nested- quote entry_point input). ovoscope.yml now uses install_extras=test now that pyproject.toml declares a [test] optional-dependencies group. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: expand OVOS template syntax in vocab entries
Wire ovos_utils.bracket_expansion.expand_template into the vocab
registration path so a single entry like
turn (on|off) the [bright] lights
is expanded into all concrete surface forms before being handed to
the adapt engine. This brings adapt in line with the rest of the
OVOS stack which already supports (a|b) alternatives and [opt]
optionals in templates.
* refactor: inline bracket-expansion helper, drop ovos-utils import
Replace the ovos_utils.bracket_expansion import with a local
ovos_adapt._bracket_expansion module so the plugin does not need
ovos-utils as a runtime dependency just for two regex helpers.
* chore: remove accidentally committed AUDIT.md and egg-info
* refactor: simplify expand_template dedup using set
Order does not matter when feeding training data to the adapt engine;
drop the seen-tracker boilerplate in favour of a plain set.
* refactor: use ovos_utils.bracket_expansion instead of a local copy
ovos-utils is already a dependency, so import expand_template from
ovos_utils.bracket_expansion and drop the inline reimplementation.
Restore pyproject.toml.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: expand OVOS template syntax in engine.register_entity
Move bracket-template expansion from the OPM pipeline wrapper into
IntentDeterminationEngine.register_entity so it applies to every
caller, including DomainIntentDeterminationEngine and direct engine
use, not just the pipeline path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: vendor expand_template in the engine module
Keep IntentDeterminationEngine standalone: port the single
expand_template helper into engine.py instead of importing it from
ovos-utils, which is only required by the optional opm.py pipeline
entrypoint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a docs/ guide covering the Adapt pipeline plugin end to end: concepts and theory for newcomers, a quickstart, the IntentBuilder reference with worked examples, configuration, the messagebus protocol, and an internals page deriving the confidence formula. Link it from the README. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: expose DomainIntentDeterminationEngine as opm.pipeline entry point
Add `DomainAdaptPipeline`, a sibling of `AdaptPipeline` that wraps
adapt's `DomainIntentDeterminationEngine`. Each `skill_id` gets its own
sub-engine ("domain"); at match time every domain is scored in
parallel and a global argmax over the union of candidates wins. No
top-level router is involved.
Intent / vocab / regex registrations are routed by the skill_id prefix
of the intent label (`skill_id:intent_name`). Configurable via
`intents.ovos_adapt_domain_pipeline`.
* test: add flat-vs-domain engine benchmark
Port the keyword-intent benchmark dataset and add a comparison harness
that runs the same vocabulary and intents through IntentDeterminationEngine
and DomainIntentDeterminationEngine. Results are documented in
docs/benchmark.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style: drop extraneous f-string prefix in benchmark
* test: add entity-overlap cases and flat-vs-domain head-to-head
Extend the benchmark dataset with utterances built around words
registered under multiple entity types across domains, the only inputs
where per-domain trie isolation can change tagging. Add a head-to-head
report listing the cases where the two engines disagree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: add two-stage hierarchical engine to the benchmark
Add a third runner that classifies the domain first, then resolves the
intent within only that domain, plus a stage-1 routing-accuracy metric.
Two-stage routing scores 74.5% against 79.3% for flat and parallel-domain:
hard routing misroutes 15% of utterances unrecoverably.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: redesign benchmark dataset with two-slot intents
Rework the dataset around two-slot intents (shared ACTION keyword plus
domain-distinctive OBJECT keyword) so single stray keywords no longer
over-trigger and domains are lexically separable. Replace the stage-1
router with a keyword-coverage classifier, which routes 98% of cases
correctly and lets the hierarchical engine suppress cross-domain false
positives.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: add discriminating cases that separate the three engines
Add overlapping vocabulary (shared temperature keyword, optional room
slots) and three hand-crafted case sections, each built to give one
topology a clear edge: routing-hard commands where flat and domain beat
hierarchical, two-clause utterances where flat and domain diverge, and
bare-keyword non-commands where hierarchical's gate suppresses false
positives. The three engines now score 87.2 / 88.2 / 90.3 percent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: frame the engine comparison as a tuned reference, not a benchmark
State plainly that the dataset is hand-tuned to surface prediction
differences between the topologies, that the accuracy ordering is an
artifact of the discriminating-section mix, and how the dataset can be
composed to favour any engine.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add HierarchicalIntentDeterminationEngine and pipeline
Make the two-stage hierarchical engine a first-class member of the
package, consistent with the flat and domain engines: a
HierarchicalIntentDeterminationEngine subclass of
DomainIntentDeterminationEngine, a HierarchicalAdaptPipeline subclass of
DomainAdaptPipeline, and a matching opm.pipeline entry point. The
benchmark now drives all three through the same engine API instead of
ad-hoc classifier glue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address CodeRabbit review on PR #37
- Serialise domain/parser reads and mutations under self.lock in the
domain pipeline (candidate gathering, detach_intent, shutdown).
- Route regex vocab registrations by entity_type, not regex_str, so
_resolve_entity_domain receives the prefix key it expects.
- Validate the intent->domain map is one-to-one before building it.
- Guard the stage-1 routing percentage against an empty match set.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: cover the domain and hierarchical pipelines
Add docs/pipelines.md describing the three pipeline variants and when
to use each, and wire it into the index, configuration, internals, and
README alongside the engine-comparison reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…42) Replace ovos_utils.lang.standardize_lang_tag (macro=True by default) with ovos_spec_tools.standardize_lang, and replace the local langcodes.closest_match + score<10 reconciliation in _get_closest_lang with ovos_spec_tools.closest_lang. Engine buckets are now keyed by the full normalized BCP-47 tag (e.g. en-US) rather than the macro language (e.g. en). Registration handlers reconcile the incoming lang to an existing bucket via closest_lang, matching the policy used at match time. BREAKING CHANGE: adapt engines now bucket by full BCP-47 tag. Resource registrations whose lang differs from the configured pipeline lang (e.g. registering en-GB vocab into an en-US pipeline) previously collided by coincidence after macro-stripping and now go through the per-match closest_lang reconciliation; configurations that depended on the silent macro-collapse should add a matching entry to secondary_langs or rely on closest_lang's region tolerance. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deps): allow ovos-workshop 9.x (widen <9.0.0 -> <10.0.0) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(deps): drop ovos-workshop dependency The adapt parser vendored its matching Intent/IntentBuilder/open_intent_envelope from ovos-workshop, which forced an ovos-workshop version cap and coupled this parser to a higher-level package. These primitives are native to the adapt parser (the engine calls Intent.validate_with_tags), so they now live in ovos_adapt.intent directly. - ovos_adapt/intent.py: own the full matching Intent + IntentBuilder (with excludes support) and open_intent_envelope instead of importing from ovos_workshop.intents - ovos_adapt/opm.py: import open_intent_envelope from ovos_adapt.intent - tests: import IntentBuilder/Intent from ovos_adapt.intent - pyproject: remove ovos-workshop dependency entirely (no cap to widen) ovos-spec-tools (closest_lang/standardize_lang) is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: consume ovos-spec-tools intent primitives instead of duplicating ovos_adapt.intent re-implemented the declarative INTENT-4 Intent / IntentBuilder / open_intent_envelope from scratch even though ovos-spec-tools (already a declared dependency) provides the canonical versions. Only the adapt-engine tag-matching logic is genuinely adapt-specific. - Intent now subclasses ovos_spec_tools.intent.Intent, inheriting the name/requires/at_least_one/optional/excludes role lists and to_keyword_payload emission; it adds ONLY validate / validate_with_tags (plus the private tag-search helpers). adapt Intent instances are now isinstance(spec_tools.Intent) and expose the INTENT-4 keyword-payload surface. - IntentBuilder subclasses ovos_spec_tools.intent.IntentBuilder and overrides only build() to return adapt's matching Intent. - open_intent_envelope reuses the spec-tools parser (which accepts both legacy and INTENT-4 §5.2 wire keys) and re-wraps as adapt's Intent. - Module helpers (is_entity, find_first_tag, find_next_tag, choose_1_from_each, resolve_one_of) are unchanged. - Floor ovos-spec-tools to >=0.16.0a1 (first release carrying the consumed intent primitives). spec-tools' Intent was cleanly subclassable: plain class (no slots/ frozen), constructor signature-compatible, and its field normalization ((type, attr) pairs for requires/optional, name tuples for at_least_one, bare names for excludes) is exactly what adapt's validate_with_tags reads. No spec-tools change required. All 93 tests pass unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ovos-bus-client 2.4.x (Session.blacklisted_intents/blacklisted_skills are optional SESSION-1 fields) can surface either blacklist as None, so the 'intent_type not in sess.blacklisted_intents' / 'skill not in sess.blacklisted_skills' membership tests in match_intent raised 'TypeError: argument of type NoneType is not iterable' and aborted every adapt match (the utterance then fell through to complete_intent_failure). Treat a None blacklist as empty. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…stration (#48) * fix: coerce foreign Intents at registration so direct registration works ovos-workshop re-exports the bare ovos-spec-tools IntentBuilder, whose Intent carries the same fields but lacks adapt's matching API (validate / validate_with_tags). Skills registering over the bus are unaffected (open_intent_envelope already rebuilds an adapt Intent from the serialized message), but direct in-process register_intent_parser of such an Intent raised ValueError. Rebuild any matching-API-less but intent-shaped parser as an adapt Intent before the interface check; native adapt Intents pass through unchanged and non-intents still raise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: declare bus-client runtime floor + lift pm/spec-tools floors opm.py imports ovos_bus_client (MessageBusClient/SessionManager/Message) at runtime; declare it directly with the migration-capable prerelease floor (>=2.5.1a1,<3.0.0). That floor only resolves without --pre when the ovos-plugin-manager floor is also lifted to the 2.x line (>=2.4.0a1), otherwise pip selects an old pm that caps ovos-bus-client <2.0. Raise the ovos-spec-tools floor to the documented baseline 0.16.1a2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) * feat: consume OVOS-INTENT-4 keyword registration (alongside legacy) Subscribe to the OVOS-INTENT-4 spec registration topics (ovos.intent.register.keyword, ovos.entity.register, the deregister trio, and intent enable/disable) in addition to the legacy register_vocab / register_intent / detach_* handlers, which are kept untouched so un-migrated skills keep working. The keyword handler translates the consolidated §5 payload into adapt's split vocab + IntentBuilder model: required -> require, optional -> optionally, one_of groups -> one_of, excluded -> exclude. Inline samples are registered as adapt entities (which expand INTENT-1 (a|b)/[opt] templates), namespaced by skill_id so detach-by-skill reaches them. Malformed payloads (no required and no one_of, missing samples) are dropped with a WARN per §5.3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(deps): bump to published merged versions (spec-tools 0.16.1a2, allow padacioso 2.0 / workshop 9) * test: add INTENT-4 consumer e2e proving spec-topic register + match Adds test/end2end/test_intent4_consume_e2e.py: boots a real MiniCroft on the adapt pipeline (ovoscope E2EPipelineHarness) and asserts adapt CONSUMES the OVOS-INTENT-4 spec registration topics, not just the legacy bus events: - §5 ovos.intent.register.keyword -> utterance matches (one_of, excluded) - legacy register_vocab/register_intent still matches (back-compat) - §8.2 ovos.intent.deregister / §8.4 ovos.skill.deregister remove the intent - §8.5 ovos.intent.disable suppresses + ovos.intent.enable re-arms - §11 the keyword engine does NOT match a template-topic registration xfail: §8.5 disable is registration-scoped in the spec but adapt scopes it to the disable Message's Session, so a cross-session disable does not suppress. Also fixes a latent crash exposed by the pinned stack: match_intent and the spec disable/enable handlers assumed Session.blacklisted_intents was a list, but ovos-bus-client>=2.4.0a1 defaults it to None -> 'argument of type NoneType is not iterable'. Guarded all three sites. Wires test/end2end/ into the ovoscope workflow and pins the e2e stack floor (ovos-bus-client>=2.4.0a1, ovos-spec-tools>=0.16.1a2) in the test extra. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(test): pin ovos-workshop 9.0.0a1 to dodge IntentBuilder regression ovos-workshop 9.0.1a2 changed IntentBuilder.build() to return an ovos_spec_tools.intent.Intent that lacks .validate / .validate_with_tags. ovos_adapt.engine.register_intent_parser enforces that duck-type, so under 9.0.1a2 *every* adapt registration (legacy register_intent AND the new INTENT-4 ovos.intent.register.keyword) is rejected with "... is not an intent parser", reddening the whole ovoscope e2e suite. The bus-client>=2.4.0a1 floor drags the latest workshop prerelease (9.0.1a2) in; pin the last good one (9.0.0a1, which still satisfies bus-client>=2.4.0a1) in the test extra until workshop restores the validating Intent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: use adapt's own IntentBuilder in legacy-flow test test_legacy_flow_still_works imported ovos_workshop.intents.IntentBuilder, but the coverage job installs the package with no extras (install_extras: '') so ovos-workshop is absent there, failing collection. ovos_adapt.intent .IntentBuilder is the adapt-native drop-in (same serialized __dict__), so the test no longer needs ovos-workshop to exercise the legacy register_intent path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: declare bus-client runtime floor + lift pm/test floors for 2.x opm.py imports ovos_bus_client at runtime; declare it directly. The prerelease floor (>=2.5.1a1) only resolves without --pre when the ovos-plugin-manager floor is also lifted to the 2.x line (>=2.4.0a1), otherwise pip selects an old pm that caps ovos-bus-client <2.0. Bump the [test] INTENT-4 e2e bus-client floor 2.4.0a1 -> 2.5.1a1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: default config value related PR: OpenVoiceOS/ovos-config#264 * fix: restore core_config reference broken by the config-default change The __init__ change replaced 'core_config = Configuration()' with an 'intents'-scoped lookup but left lines reading core_config.get('lang') / .get('secondary_langs') → NameError. Keep both: core_config for lang/secondary_langs, intent_config for the plugin's own config block.
spec-tools crossed to 1.x; bus-client 2.6.0a1 and the rest of the stack now require ovos-spec-tools>=1.1.0a1. Drop the <1.0.0 cap (keep the floor) so the stack resolves. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human review requested!